#include "materialeditor.h"
#include <QAbstractItemModel>
#include <QList>
#include <gfx/material.h>
#include <gfx/texturepack.h>
#include <genericdeletedialog.h>
#include <material_editor/selectmaterialdatadialog.h>
#include <QMenu>


class TexturePack
{
private:
  ceTexturePack*  _pack;
  Texture*  _textures[RP_Count][TS_Count];

  MaterialManager*  _manager;

public:
  TexturePack (MaterialManager* manager)
		: _pack (0)
		, _manager (manager)
	{
	}

  ~TexturePack ()
	{
		CE_UNSET(_pack);
	}

  void SetTexturePack (ceTexturePack* pack)
	{
		for (unsigned rp=0; rp<RP_Count; rp++)
			{
				for (unsigned ts=0; ts<TS_Count; ts++)
					{
						iTexture* txt = pack->GetTexture((ceRenderPass)rp, (ceTextureStage)ts);
						_textures[rp][ts] = _manager->FindTexture(txt);
					}
			}
	}


  void SetTexture (ceRenderPass pass, ceTextureStage stage, Texture* texture)
	{
		_textures[pass][stage] = texture;
	}

  Texture *GetTexture (ceRenderPass pass, ceTextureStage stage)
	{
		return _textures[pass][stage];
	}

};

class TexturePackTreeModel : public QAbstractItemModel
{
private:
  TexturePack*  _pack;

  QColor        _backgroundColors[3][RP_Count];

public:
  TexturePackTreeModel ()
    : _pack (0)
  {
    unsigned l0 = 0xa0;
    unsigned l1 = 0xff;

    unsigned d0 = 0x80;
    unsigned d1 = 0xff;

    l0 = 0xc0;
    l1 = 0xd0;

    d0 = 0xb0;
    d1 = 0xc0;

    unsigned g0 = 0x80;
    unsigned g1 = 0xa0;
    _backgroundColors[0][RP_Diffuse]       = QColor (l0, l0, l1);
    _backgroundColors[0][RP_Depth]         = QColor (l0, l1, l0);
    _backgroundColors[0][RP_Shadow]        = QColor (l1, l0, l0);
    _backgroundColors[0][RP_StencilVolume] = QColor (l1, l1, l0);

    _backgroundColors[1][RP_Diffuse]       = QColor (d0, d0, d1);
    _backgroundColors[1][RP_Depth]         = QColor (d0, d1, d0);
    _backgroundColors[1][RP_Shadow]        = QColor (d1, d0, d0);
    _backgroundColors[1][RP_StencilVolume] = QColor (d1, d1, d0);

    _backgroundColors[2][RP_Diffuse]       = QColor (g0, g0, g1);
    _backgroundColors[2][RP_Depth]         = QColor (g0, g1, g0);
    _backgroundColors[2][RP_Shadow]        = QColor (g1, g0, g0);
    _backgroundColors[2][RP_StencilVolume] = QColor (g1, g1, g0);
  }

  void SetTexturePack (TexturePack* pack)
  {
    if(_pack && pack != _pack) delete _pack;
    _pack = pack;
    reset ();
  }

  TexturePack* GetTexturePack () const
  {
    return _pack;
  }

  virtual int columnCount (const QModelIndex& parent = QModelIndex()) const
  {
    return 2;
  }

  Texture* GetTexture (const QModelIndex& index)
  {
    if (!index.isValid())
      {
        return 0;
      }

    int p = index.internalId();

    quint8 rp = (p & 0xff00) >> 8;
    quint8 ts = (p & 0xff);

    if (ts == 0xff)
      {
        return 0;
      }

    return _pack->GetTexture((ceRenderPass)rp, (ceTextureStage)ts);
  }

  virtual QVariant data (const QModelIndex& index, int role = Qt::DisplayRole) const
  {
    if (!_pack) return QVariant();
    if (!index.isValid()) return QVariant ();

    int p = index.internalId();

    quint8 rp = (p & 0xff00) >> 8;
    quint8 ts = (p & 0xff);

    if (ts == 0xff)
      {
        if (role == Qt::DisplayRole && index.column() == 0)
          {
            // render phase value
            switch (rp)
              {
              case RP_Depth:
                return QVariant ("Depth");
              case RP_Diffuse:
                return QVariant ("Diffuse");
              case RP_Shadow:
                return QVariant ("Shadow");
              case RP_StencilVolume:
                return QVariant ("StencilVolume");
              }
          }
        else if (role == Qt::DisplayRole && index.column () == 1)
          {
            unsigned num = 0;
            for (unsigned i=0; i<TS_Count; i++)
              {
                Texture* texture = _pack->GetTexture((ceRenderPass)rp, (ceTextureStage)i);
                if (texture)
                  {
                    num++;
                  }
              }
            if (num == 1)
              {
                return QVariant (QString ("%1 Texture assigned").arg(num));
              }
            else if (num > 0)
              {
                return QVariant (QString ("%1 Textures assigned").arg(num));
              }
            return QVariant ("");
          }
      }
    else
      {

        if (role == Qt::DisplayRole && index.column() == 0)
          {
            QString name = QString ("Stage %1").arg (ts+1);
            return QVariant (name);
          }
        else if (role == Qt::DisplayRole && index.column() == 1)
          {
            Texture* texture = _pack->GetTexture((ceRenderPass)rp, (ceTextureStage)ts);
            if (!texture)
              {
                return QVariant ("<unassigned>");
              }
            else
              {
                return QVariant (texture->GetCategory().GetFullName() + "." + texture->GetFullname());
              }
          }
      }
    if (role == Qt::BackgroundColorRole)
      {
        if (ts == 0xff)
          {
            return QVariant (_backgroundColors[2][rp]);
          }
        else
          {
            return QVariant (_backgroundColors[ts % 2][rp]);
          }

      }

    return QVariant ();
  }

  virtual QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) const
  {
    if (!_pack) return QModelIndex();
    if (!parent.isValid())
      {
        if (row >= RP_Count)
          {
            return QModelIndex();
          }

        int id = ((row & 0xff) << 8) | 0xff;
        return createIndex(row, column, id);
      }
    else
      {
        int id = parent.internalId();
        if ((id & 0xff) != 0xff)
          {
            return QModelIndex();
          }

        // release the last 8 bits
        id = (id & 0xff00) | (row & 0xff);
        return createIndex(row, column, id);
      }
    return QModelIndex();
  }

  QVariant headerData(int section, Qt::Orientation orientation, int role) const
  {
    if (role == Qt::DisplayRole)
      {
        switch (section)
          {
          case 0:
            return "Stage";
          case 1:
            return "Texture";
          }
      }

    return QVariant ();
  }

  virtual QModelIndex parent (const QModelIndex& index) const
  {
    if (!_pack) return QModelIndex();
    if (!index.isValid())
      {
        return QModelIndex ();
      }
    int id = index.internalId();
    if ((id & 0xff) == 0xff)
      {
        return QModelIndex();
      }
    else
      {
        id |= 0xff;
        unsigned row = (id & 0xff00) >> 8;
        return createIndex(row, 0, id);
      }

    return QModelIndex ();
  }

  virtual int rowCount (const QModelIndex& parent = QModelIndex()) const
  {
    if (!_pack) return 0;
    if (!parent.isValid())
      {
        return RP_Count;
      }
    int id = parent.internalId();
    if ((id & 0xff) == 0xff)
      {
        return TS_Count;
      }

    return 0;
  }

  void SetTexture (const QModelIndex& index, Texture* texture)
  {
    if (!index.isValid())
      {
        return;
      }
    int id = index.internalId();
    if ((id & 0xff) == 0xff)
      {
        return;
      }

    ceRenderPass rp = (ceRenderPass)((id & 0xff00) >> 8);
    ceTextureStage ts = (ceTextureStage)(id & 0xff);

    _pack->SetTexture(rp, ts, texture);

    emit dataChanged(createIndex(index.row(), 0, id), createIndex(index.row(), 1, id));
  }
};


class EffectTreeModel : public QAbstractItemModel
{
private:
	Effect*				_effect;

public:
  EffectTreeModel ()
    : _effect (0)
  {
  }

	
	Effect* GetEffect ()
	{
		return _effect;
	}

  virtual int columnCount (const QModelIndex& parent = QModelIndex()) const
  {
    return 2;
  }

  virtual QVariant data (const QModelIndex& index, int role = Qt::DisplayRole) const
  {
    if (!index.isValid()) return QVariant ();

		if (role == Qt::DisplayRole)
			{
				switch (index.column ())
					{
					case 0:
						return QVariant ("Effect");
					case 1:
						do 
							{
								if (_effect)
									{
										return QVariant (_effect->GetCategory().GetFullName () + "." + _effect->GetFullname ());
									}
								else
									{
										return QVariant ("<unassigned>");
									}
							}
						while (false);
					}
			}

    return QVariant ();
  }

  virtual QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) const
  {
		if (parent.isValid ()) return QModelIndex();
		if (row != 0) return QModelIndex();
		if (column < 0 || column > 1) return QModelIndex ();
		return createIndex (row, column, 0);
  }

  QVariant headerData(int section, Qt::Orientation orientation, int role) const
  {
    if (role == Qt::DisplayRole)
      {
        switch (section)
          {
          case 0:
            return "Effect";
          case 1:
            return "Texture";
          }
      }

    return QVariant ();
  }

  virtual QModelIndex parent (const QModelIndex& index) const
  {
		return QModelIndex ();
  }

  virtual int rowCount (const QModelIndex& parent = QModelIndex()) const
  {
		if (parent.isValid ()) return 0;
		return 1;
  }

	void SetEffect (Effect* effect)
	{
		_effect = effect;

		emit dataChanged (createIndex (0, 0, 0), createIndex (0, 1, 0));
	}
};



MaterialEditor::MaterialEditor(Material *material, MaterialView *view, QWidget *parent)
  : BaseMaterialEditorWidget(parent)
  , _materialView (view)
  , _material(0)
{
  _gui.setupUi (this);

  _groupView = new GroupTreeItemView(this);
  QGridLayout* layout = new QGridLayout(_gui.widgetGroupView);
  layout->setContentsMargins(QMargins (0, 0, 0, 0));
  layout->addWidget (_groupView, 0, 0);
  setBackgroundRole(QPalette::Window);


  layout = new QGridLayout(_gui.frameAmbient);
  layout->setContentsMargins(QMargins (0, 0, 0, 0));
  _cpAmbient = new ColorPicker (this);
  layout->addWidget(_cpAmbient, 0, 0);

  layout = new QGridLayout(_gui.frameDiffuse);
  layout->setContentsMargins(QMargins (0, 0, 0, 0));
  _cpDiffuse= new ColorPicker (this);
  layout->addWidget(_cpDiffuse, 0, 0);

  layout = new QGridLayout(_gui.frameSpecular);
  layout->setContentsMargins(QMargins (0, 0, 0, 0));
  _cpSpecular = new ColorPicker (this);
  layout->addWidget(_cpSpecular, 0, 0);

  layout = new QGridLayout(_gui.frameEmission);
  layout->setContentsMargins(QMargins (0, 0, 0, 0));
  _cpEmission = new ColorPicker (this);
  layout->addWidget(_cpEmission, 0, 0);

  connect (_cpAmbient, SIGNAL(changed()), this, SLOT(MaterialSpecChanged()));
  connect (_cpDiffuse, SIGNAL(changed()), this, SLOT(MaterialSpecChanged()));
  connect (_cpSpecular, SIGNAL(changed()), this, SLOT(MaterialSpecChanged()));
  connect (_cpEmission, SIGNAL(changed()), this, SLOT(MaterialSpecChanged()));

  _actions[Action_ShowEffect] = new QAction (tr("Show Effect"), this);
  _actions[Action_ShowTexture] = new QAction (tr ("Show Textures"), this);

  connect (_actions[Action_ShowTexture], SIGNAL(triggered()), this, SLOT(actionsShowTexture_triggered()));

  _texturePackTreeModel = new TexturePackTreeModel ();
  _gui.treeViewTexturePack->setModel(_texturePackTreeModel);

	_effectTreeModel = new EffectTreeModel ();
	_gui.treeViewEffect->setModel(_effectTreeModel);

  SetMaterial(material);

}


void MaterialEditor::SetMaterial(Material *material)
{
  _material = material;
  _groupView->SetGroupTreeItem(&material->GetCategory());

  SetItem(material);

  Load ();
}


Material* MaterialEditor::GetMaterial()
{
  return _material;
}

void MaterialEditor::MaterialSpecChanged()
{
  SetDirty(true);
}

void MaterialEditor::on_checkBoxTransparency_stateChanged(int state)
{
  MaterialSpecChanged();
}

void MaterialEditor::on_checkBoxWriteDepth_stateChanged(int state)
{
  MaterialSpecChanged();
}

void MaterialEditor::on_checkBoxTestDepth_stateChanged(int state)
{
  MaterialSpecChanged();
}

void MaterialEditor::on_doubleSpinBoxShininess_valueChanged(double d)
{
  MaterialSpecChanged();
}

void MaterialEditor::on_treeViewTexturePack_customContextMenuRequested(const QPoint &pos)
{
  QPoint gpos = _gui.treeViewTexturePack->mapToGlobal(pos);

  gpos.setY(gpos.y() + _gui.treeViewTexturePack->header()->height());

  QMenu* menu = new QMenu();
  menu->addAction(_actions[Action_ShowTexture]);
  menu->popup(gpos);
}

void MaterialEditor::on_treeViewTexturePack_activated(const QModelIndex &index)
{
  if (!index.isValid())
    {
      return;
    }

  int id = index.internalId();
  if ((id & 0xff) == 0xff)
    {
      return;
    }

  AllTextureDelegator del (_materialView->GetMaterialManager());
  SelectMaterialDataDialog dlg (this);
  dlg.SetClearButtonVisible(true);
  dlg.SetDelegator(&del);
  if (dlg.exec() == QDialog::Accepted)
    {
      Texture* texture = static_cast<Texture*>(dlg.GetData());
      _texturePackTreeModel->SetTexture (index, texture);
      SetDirty(true);
    }
}

void MaterialEditor::actionsShowTexture_triggered()
{
  QModelIndex index = _gui.treeViewTexturePack->currentIndex();
  if (!index.isValid())
    {
      return;
    }

  Texture* texture = _texturePackTreeModel->GetTexture(index);
  if (!texture)
    {
      return;
    }
  _materialView->EditTexture(texture);

}

void MaterialEditor::on_treeViewEffect_activated(const QModelIndex &index)
{
  if (!index.isValid())
    {
      return;
    }

  if (index.row() != 0)
    {
      return;
    }

  AllEffectDelegator del (_materialView->GetMaterialManager());
  SelectMaterialDataDialog dlg (this);
  dlg.SetClearButtonVisible(true);
  dlg.SetDelegator(&del);
  if (dlg.exec() == QDialog::Accepted)
    {
      Effect* effect = static_cast<Effect*>(dlg.GetData());
      _effectTreeModel->SetEffect(effect);
      SetDirty(true);
    }


}


void MaterialEditor::Load ()
{
  ceMaterial* mat = _material->GetMaterial();
  ceMaterialSpec& spec = mat->GetMaterialSpec();

  _cpAmbient->SetColor(spec.Ambient);
  _cpDiffuse->SetColor(spec.Diffuse);
  _cpSpecular->SetColor(spec.Specular);
  _gui.doubleSpinBoxShininess->setValue (spec.Shininess);
  _cpEmission->SetColor(spec.Emission);
  _gui.checkBoxTransparency->setChecked(spec.Transparency);
  _gui.checkBoxWriteDepth->setChecked(spec.WriteDepth);
  _gui.checkBoxTestDepth->setChecked(spec.TestDepth);


  ceTexturePack* pck = mat->GetTextures();
  TexturePack* pack = new TexturePack(_materialView->GetMaterialManager());
  pack->SetTexturePack(pck);
  _texturePackTreeModel->SetTexturePack(pack);

  Effect* effect = _materialView->GetMaterialManager()->FindEffect (mat->GetEffect());
	_effectTreeModel->SetEffect(effect);

  SetDirty(false);
}

void MaterialEditor::Save()
{
  ceMaterial* mat = _material->GetMaterial();
  ceMaterialSpec& spec = mat->GetMaterialSpec();

  spec.Ambient = _cpAmbient->GetColor();
  spec.Diffuse = _cpDiffuse->GetColor();
  spec.Specular = _cpSpecular->GetColor();
  spec.Shininess = _gui.doubleSpinBoxShininess->value();
  spec.Emission = _cpEmission->GetColor();
  spec.Transparency = _gui.checkBoxTransparency->isChecked();
  spec.WriteDepth = _gui.checkBoxWriteDepth->isChecked();
  spec.TestDepth = _gui.checkBoxTestDepth->isChecked();

  ceTexturePack* pack = mat->GetTextures();
  for (unsigned rp=0; rp<RP_Count; rp++)
    {
      for (unsigned ts=0; ts<TS_Count; ts++)
        {
          Texture* texture = _texturePackTreeModel->GetTexturePack()->GetTexture((ceRenderPass)rp, (ceTextureStage)ts);
          iTexture* txt = 0;
          if (texture)
            {
              txt =  texture->GetTexture();
            }
          pack->SetTexture((ceRenderPass)rp, (ceTextureStage)ts, txt);
        }
    }

	Effect* effect = _effectTreeModel->GetEffect ();
  _material->SetEffect(effect);

  SetDirty(false);
}

